﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using CoreVirtuoso;
using CoreVirtuoso.Config;
using CoreVirtuoso.Descriptions;
using CoreVirtuoso.Helpers;
using VDS.RDF.Query;
using VirtuosoDal.Entities;

namespace VirtuosoDal.Helpers
{
    public class SparqlResultSetConverter
    {
        public static List<Event> FromTriples(SparqlResultSet sparqlResultSet, List<string> uris, string language)
        {
            var events = new List<Event>();

            if (sparqlResultSet == null)
                return events;

            string subjectVariable = sparqlResultSet.Variables.ElementAt(0);
            string predicateVariable = sparqlResultSet.Variables.ElementAt(1);
            string objectVariable = sparqlResultSet.Variables.ElementAt(2);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string subject = result.Value(subjectVariable).ToString();
                string predicate = result.Value(predicateVariable).ToString();
                string @object = LiteralHelper.GetLiteral(result.Value(objectVariable));

                if (!events.Exists(e => e.Uri == subject))
                    events.Add(new Event(subject));

                Event @event = events.Single(e => e.Uri == subject);

                if (predicate == EventsOntologyUris.TitlePropertyUri)
                    @event.Title = @object;
                else if (predicate == EventsOntologyUris.DescriptionPropertyUri)
                    @event.Description = @object;
                else if (predicate == EventsOntologyUris.StartDateTimePropertyUri)
                    @event.StartDateTimeOffset = DateTimeOffset.Parse(@object);
                else if (predicate == EventsOntologyUris.EndDateTimePropertyUri)
                    @event.EndDateTimeOffset = DateTimeOffset.Parse(@object);
                else if (predicate == EventsOntologyUris.CityPropertyUri)
                    @event.CityUri = @object;
                else if (predicate == EventsOntologyUris.PlacePropertyUri)
                    @event.PlaceUri = @object;
                else if (predicate == EventsOntologyUris.CreatedByPropertyUri)
                    @event.Creator = @object;
                else if (predicate == EventsOntologyUris.CreationDateTimePropertyUri)
                    //ToDo: Find DateTimeOffset parsing code and call it here
                    @event.CreationDateTime = DateTime.Parse(@object);
                else if (predicate == EventsOntologyUris.LastEditingDateTimePropertyUri)
                    @event.LastEditingDateTime = DateTime.Parse(@object);
                else if (predicate == EventsOntologyUris.EventTypePropertyUri)
                    @event.Type = @object;
                else if (predicate == EventsOntologyUris.EventCategoryPropertyUri)
                    @event.Categories.Add(@object);
                else
                {
                    if (!@event.AdditionalPropertiesWithValues.ContainsKey(predicate))
                        @event.AdditionalPropertiesWithValues[predicate] = new List<string>();

                    @event.AdditionalPropertiesWithValues[predicate].Add(@object);
                }
            }

            EventExtraFillingHelper.FillGeoNamesValuesForEvents(events, language);
            EventExtraFillingHelper.FillLgdValuesForEvents(events);

            return uris.Select(u => events.Where(e => e.Uri == u).Single()).ToList();
        }

        public static Dictionary<string, EventPropertyDescription> FromAllEventPropertyDescriptions(
           SparqlResultSet sparqlResultSet)
        {
            Dictionary<string, EventPropertyDescription> dictionary = new Dictionary<string, EventPropertyDescription>();
            if (sparqlResultSet == null)
                return dictionary;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string propertyVariable = variables.ElementAt(0);
            string labelVariable = variables.ElementAt(1);
            string isMultiValuedVariable = variables.ElementAt(2);
            string isHierarchyVariable = variables.ElementAt(3);
            string hierarchyRootVariable = variables.ElementAt(4);
            string isNotAutomaticVariable = variables.ElementAt(5);
            string isMultilineVariable = variables.ElementAt(6);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string propertyUri = LiteralHelper.GetLiteral(result.Value(propertyVariable));
                string label = LiteralHelper.GetLiteral(result.Value(labelVariable));

                bool isMultiValued = false;
                object isMultiValuedObject = result.Value(isMultiValuedVariable);
                if (isMultiValuedObject != null)
                {
                    string isMultiValuedString = LiteralHelper.GetLiteral(isMultiValuedObject);
                    isMultiValued = Boolean.Parse(isMultiValuedString);
                }

                bool isHierarchy = false;
                object isHierarchyObject = result.Value(isHierarchyVariable);
                if (isHierarchyObject != null)
                {
                    string isHierarchyString = LiteralHelper.GetLiteral(isHierarchyObject);
                    isHierarchy = Boolean.Parse(isHierarchyString);
                }

                string hierarchyRootUri = null;
                object hierarchyRootUriObject = result.Value(hierarchyRootVariable);
                if (hierarchyRootUriObject != null)
                    hierarchyRootUri = LiteralHelper.GetLiteral(hierarchyRootUriObject);

                bool isNotAutomatic = false;
                object isNotAutomaticObject = result.Value(isNotAutomaticVariable);
                if (isNotAutomaticObject != null)
                {
                    string isNotDisplayableString = LiteralHelper.GetLiteral(isNotAutomaticObject);
                    isNotAutomatic = Boolean.Parse(isNotDisplayableString);
                }

                bool isMultiline = false;
                object isMultilineObject = result.Value(isMultilineVariable);
                if (isMultilineObject != null)
                {
                    string isMultilineString = LiteralHelper.GetLiteral(isMultilineObject);
                    isMultiline = Boolean.Parse(isMultilineString);
                }

                dictionary.Add(propertyUri,
                               new EventPropertyDescription(propertyUri, label, isMultiValued, isHierarchy, hierarchyRootUri,
                                                            isNotAutomatic, isMultiline));
            }

            return dictionary;
        }

        public static List<string> ToListOfUris(SparqlResultSet sparqlResultSet)
        {
            List<string> list = new List<string>();

            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string uriVariable = variables.ElementAt(0);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string uri = LiteralHelper.GetLiteral(result.Value(uriVariable));

                list.Add(uri);
            }

            return list;
        }

        public static List<EntityDescription> ToListOfEntityDescription(SparqlResultSet sparqlResultSet)
        {
            List<EntityDescription> list = new List<EntityDescription>();
            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string classUriVariable = variables.ElementAt(0);
            string labelVariable = variables.ElementAt(1);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string classUri = LiteralHelper.GetLiteral(result.Value(classUriVariable));
                string label = LiteralHelper.GetLiteral(result.Value(labelVariable));

                list.Add(new EntityDescription(classUri, label));
            }

            return list;
        }

        public static List<ObjectDescription> ToListOfObjectDescription(SparqlResultSet sparqlResultSet)
        {
            List<ObjectDescription> list = new List<ObjectDescription>();
            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string uriVariable = variables.ElementAt(0);
            string nameVariable = variables.ElementAt(1);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string uri = result.Value(uriVariable).ToString();
                string name = result.Value(nameVariable).ToString();

                list.Add(new ObjectDescription(uri, name));
            }

            return list;
        }

        public static List<GeoObjectDescription> ToListOfGeoObjectDescription(SparqlResultSet sparqlResultSet)
        {
            List<GeoObjectDescription> list = new List<GeoObjectDescription>();
            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string cityUriVariable = variables.ElementAt(0);
            string cityNameVariable = variables.ElementAt(1);
            string cityLatitudeVariable = variables.ElementAt(2);
            string cityLongitudeVariable = variables.ElementAt(3);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string cityUri = LiteralHelper.GetLiteral(result.Value(cityUriVariable));

                string cityName = LiteralHelper.GetLiteral(result.Value(cityNameVariable));

                string cityLatitudeString = LiteralHelper.GetLiteral(result.Value(cityLatitudeVariable));
                double cityLatitude = Double.Parse(cityLatitudeString, CultureInfo.InvariantCulture);

                string cityLongitudeString = LiteralHelper.GetLiteral(result.Value(cityLongitudeVariable));
                double cityLongitude = Double.Parse(cityLongitudeString, CultureInfo.InvariantCulture);

                list.Add(new GeoObjectDescription(cityUri, cityName, cityLatitude, cityLongitude));
            }

            return list;
        }

        public static List<CityDescription> ToListOfGeoNamesCityDescription(SparqlResultSet sparqlResultSet)
        {
            List<CityDescription> list = new List<CityDescription>();
            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string cityUriVariable = variables.ElementAt(0);
            string cityNameVariable = variables.ElementAt(1);
            string cityPreferredNameVariable = variables.ElementAt(2);
            string cityAlternateNameVariable = variables.ElementAt(3);
            string countryNameVariable = variables.ElementAt(4);
            string countryPreferredNameVariable = variables.ElementAt(5);
            string countryAlternateNameVariable = variables.ElementAt(6);
            string cityLatitudeVariable = variables.ElementAt(7);
            string cityLongitudeVariable = variables.ElementAt(8);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string cityUri = LiteralHelper.GetLiteral(result.Value(cityUriVariable));

                string cityName = LiteralHelper.GetLiteral(result.Value(cityNameVariable));
                string cityPreferredName = LiteralHelper.GetLiteral(result.Value(cityPreferredNameVariable));
                string cityAlternateName = LiteralHelper.GetLiteral(result.Value(cityAlternateNameVariable));

                if (!String.IsNullOrEmpty(cityPreferredName))
                    cityName = cityPreferredName;
                else if (!String.IsNullOrEmpty(cityAlternateName))
                    cityName = cityAlternateName;

                if (String.IsNullOrEmpty(cityName))
                    // ToDo: Log this situation
                    continue;

                string countryName = LiteralHelper.GetLiteral(result.Value(countryNameVariable));
                string countryPreferredName = LiteralHelper.GetLiteral(result.Value(countryPreferredNameVariable));
                string countryAlternateName = LiteralHelper.GetLiteral(result.Value(countryAlternateNameVariable));

                if (!String.IsNullOrEmpty(countryPreferredName))
                    countryName = countryPreferredName;
                else if (!String.IsNullOrEmpty(countryAlternateName))
                    countryName = countryAlternateName;

                if (String.IsNullOrEmpty(countryName))
                    // ToDo: Log this situation
                    continue;

                string cityLatitudeString = LiteralHelper.GetLiteral(result.Value(cityLatitudeVariable));
                //Parse using current culture (not InvariantCulture) as ADO.NET provider uses it for ToString call.
                //It is not case for LGD because it stores lat/long as string but not as doubles (we do it for custom
                //GeoNames dataset.
                //ToDo: Think. May be to unify lat/long storing we should store non-types literals (just strings)
                //ToDo: Do we really need parsed lat/long for CityDescription and other description types?
                double cityLatitude = Double.Parse(cityLatitudeString);

                string cityLongitudeString = LiteralHelper.GetLiteral(result.Value(cityLongitudeVariable));
                double cityLongitude = Double.Parse(cityLongitudeString);

                list.Add(new CityDescription(cityUri, cityName, cityLatitude, cityLongitude, countryName, ""));
            }

            return list;
        }

        public static List<GeoNamesObjectDescription> ToListOfGeoObjects(SparqlResultSet sparqlResultSet)
        {
            List<GeoNamesObjectDescription> list = new List<GeoNamesObjectDescription>();
            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string geoObjectUriVariable = variables.ElementAt(0);
            string geoObjectNameVariable = variables.ElementAt(1);
            string geoObjectPreferredNameVariable = variables.ElementAt(2);
            string geoObjectAlternateNameVariable = variables.ElementAt(3);
            string geoObjectIsCountryVariable = variables.ElementAt(4);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string geoObjectUri = LiteralHelper.GetLiteral(result.Value(geoObjectUriVariable));

                string geoObjectName = LiteralHelper.GetLiteral(result.Value(geoObjectNameVariable));
                string geoObjectPreferredName = LiteralHelper.GetLiteral(result.Value(geoObjectPreferredNameVariable));
                string geoObjectAlternateName = LiteralHelper.GetLiteral(result.Value(geoObjectAlternateNameVariable));

                if (!String.IsNullOrEmpty(geoObjectPreferredName))
                    geoObjectName = geoObjectPreferredName;
                else if (!String.IsNullOrEmpty(geoObjectAlternateName))
                    geoObjectName = geoObjectAlternateName;

                if (String.IsNullOrEmpty(geoObjectName))
                    // ToDo: Log this situation
                    continue;

                object isCountry = result.Value(geoObjectIsCountryVariable);
                GeoObjectType geoObjectType = GeoObjectType.City;
                if (isCountry != null)
                    geoObjectType = GeoObjectType.Country;

                list.Add(new GeoNamesObjectDescription(geoObjectUri, geoObjectName, geoObjectType));
            }

            return list;
        }

        public static List<GeoNamesCityShortDescription> ToListOfGeoNamesShortCityDescriptions(
           SparqlResultSet sparqlResultSet)
        {
            List<GeoNamesCityShortDescription> list = new List<GeoNamesCityShortDescription>();

            if (sparqlResultSet == null || sparqlResultSet.Count == 0)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;

            string cityUriVariable = variables.ElementAt(0);
            string cityNameVariable = variables.ElementAt(1);
            string cityPreferredNameVariable = variables.ElementAt(2);
            string cityAlternateNameVariable = variables.ElementAt(3);
            string countryNameVariable = variables.ElementAt(4);
            string countryPreferredNameVariable = variables.ElementAt(5);
            string countryAlternateNameVariable = variables.ElementAt(6);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string cityUri = LiteralHelper.GetLiteral(result.Value(cityUriVariable));
                string cityName = LiteralHelper.GetLiteral(result.Value(cityNameVariable));
                string cityPreferredName = LiteralHelper.GetLiteral(result.Value(cityPreferredNameVariable));
                string cityAlternateName = LiteralHelper.GetLiteral(result.Value(cityAlternateNameVariable));
                string countryName = LiteralHelper.GetLiteral(result.Value(countryNameVariable));
                string countryPreferredName = LiteralHelper.GetLiteral(result.Value(countryPreferredNameVariable));
                string countryAlternateName = LiteralHelper.GetLiteral(result.Value(countryAlternateNameVariable));

                if (!String.IsNullOrEmpty(cityPreferredName))
                    cityName = cityPreferredName;
                else if (!String.IsNullOrEmpty(cityAlternateName))
                    cityName = cityAlternateName;

                if (String.IsNullOrEmpty(cityName))
                {
                    // ToDo: Log this situation
                    ;
                }

                if (!String.IsNullOrEmpty(countryPreferredName))
                    countryName = countryPreferredName;
                else if (!String.IsNullOrEmpty(countryAlternateName))
                    countryName = countryAlternateName;

                if (String.IsNullOrEmpty(countryName))
                {
                    // ToDo: Log this situation
                    ;
                }

                list.Add(new GeoNamesCityShortDescription(cityUri, cityName, countryName));
            }

            return list;
        }

        public static List<ObjectDescription> ToListOfLgdObjectsDescription(SparqlResultSet sparqlResultSet)
        {
            List<ObjectDescription> list = new List<ObjectDescription>();

            if (sparqlResultSet == null || sparqlResultSet.Count == 0)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;

            string placeUriVariable = variables.ElementAt(0);
            string placeNameVariable = variables.ElementAt(1);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string placeUri = LiteralHelper.GetLiteral(result.Value(placeUriVariable));
                string placeName = LiteralHelper.GetLiteral(result.Value(placeNameVariable));

                list.Add(new ObjectDescription(placeUri, placeName));
            }

            return list;
        }

        public static List<GeoLabelDescription> ToListOfGeoLabelDescriptions(SparqlResultSet sparqlResultSet)
        {
            List<GeoLabelDescription> list = new List<GeoLabelDescription>();
            if (sparqlResultSet == null)
                return list;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string geoLabelUriVarialbe = variables.ElementAt(0);
            string geoLabelVariable = variables.ElementAt(1);
            string cityVariable = variables.ElementAt(2);
            string cityNameVariable = variables.ElementAt(3);
            string placeVariable = variables.ElementAt(4);
            string placeNameVariable = variables.ElementAt(5);

            foreach (SparqlResult result in sparqlResultSet)
            {
                string geoLabelUri = result.Value(geoLabelUriVarialbe).ToString();
                string geoLabel = result.Value(geoLabelVariable).ToString();
                string city = result.Value(cityVariable).ToString();
                string cityName = result.Value(cityNameVariable).ToString();
                string place = result.Value(placeVariable).ToString();
                string placeName = result.Value(placeNameVariable).ToString();

                list.Add(new GeoLabelDescription(geoLabelUri, geoLabel, city, cityName, place, placeName));
            }

            return list;
        }

        //public static void ToGeoObject(SparqlResultSet sparqlResultSet, ref string cityUri, ref string placeUri)
        //{
        //   IEnumerable<string> variables = sparqlResultSet.Variables;
        //   string cityVariable = variables.ElementAt(0);
        //   string placeVariable = variables.ElementAt(1);

        //   SparqlResult result = sparqlResultSet[0];
        //   cityUri = LiteralHelper.GetLiteral(result.Value(cityVariable));
        //   placeUri = LiteralHelper.GetLiteral(result.Value(placeVariable));
        //}

        public static string ToTimeZone(SparqlResultSet sparqlResultSet)
        {
            if (sparqlResultSet == null || sparqlResultSet.Count == 0)
                return null;

            IEnumerable<string> variables = sparqlResultSet.Variables;
            string timeZoneVariable = variables.ElementAt(0);

            return sparqlResultSet[0].Value(timeZoneVariable).ToString();
        }

        public static int ToCount(SparqlResultSet sparqlResultSet)
        {
            if (sparqlResultSet == null || sparqlResultSet.Count == 0)
                throw new Exception("Unexpected query result");

            return Convert.ToInt32(LiteralHelper.GetLiteral(sparqlResultSet[0][0]));
        }
    }
}